﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SettingsTab.xaml.cs" company="arvystate.net">
//   arvystate.net
// </copyright>
// <summary>
//   Interaction logic for SettingsTab.xaml
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace PresentationAid.Tabs
{
    using System;
    using System.AutoUpdate.Core;
    using System.AutoUpdate.Library;
    using System.AutoUpdate.Managers;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media.Animation;
    using System.Windows.Media.Imaging;

    using Elysium.Controls;

    using NLog;

    using PresentationAid.Core;
    using PresentationAid.Lib.Enums;
    using PresentationAid.Lib.GestureEngines;
    using PresentationAid.Settings;

    using MessageBox = PresentationAid.Dialog.MessageBox;

    /// <summary>
    /// Interaction logic for SettingsTab
    /// </summary>
    public partial class SettingsTab
    {
        #region Static Fields

        /// <summary>
        /// The logger.
        /// </summary>
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        #endregion

        #region Fields

        /// <summary>
        /// The update info.
        /// </summary>
        private UpdateInfo updateInfo;

        /// <summary>
        /// The _update status.
        /// </summary>
        private UpdateDialogStatus updateStatus;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SettingsTab"/> class.
        /// </summary>
        public SettingsTab()
        {
            Logger.Trace("Init: SettingsTab");

            this.updateStatus = UpdateDialogStatus.OutOfDate;

            this.InitializeComponent();

            // To make things easier, we just use binding to setting manager
            this.DataContext = SettingManager.Instance;

            UpdateManager.Instance.UpdateCheckFinished += this.OnUpdateCheckFinished;
            UpdateManager.Instance.ApplyCompleted += this.OnApplyCompleted;
            UpdateManager.Instance.StatusChanged += this.OnStatusChanged;
            UpdateManager.Instance.DownloadProgress += this.OnDownloadProgress;

            // If application auto started the checking before the control was loaded
            Logger.Trace("InitDetail: SettingsTab - UpdateManagerStatus: {0}", UpdateManager.Instance.Status.ToString());

            if (UpdateManager.Instance.Status == UpdateStatus.Checking)
            {
                this.DisplayStatus(UpdateDialogStatus.Checking);
            }
            else
            {
                // TODO: Uncomment this when releasing application
                // DateTime lastCheck = new DateTime(SettingManager.Instance.LastUpdateCheck);
                DateTime lastCheck = new DateTime(0);

                this.DisplayStatus(
                    (DateTime.Now - lastCheck).TotalDays > 1
                        ? UpdateDialogStatus.OutOfDate
                        : UpdateDialogStatus.UpToDate);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The advanced settings button click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void AdvancedSettingsButtonClick(object sender, RoutedEventArgs e)
        {
            if ((string)this.AdvancedSettingsButton.Content == "Show advanced settings...")
            {
                Storyboard s = (Storyboard)this.TryFindResource("SettingsSlideOpenAnimation");
                s.Begin();

                this.AdvancedSettingsButton.Content = "Hide advanced settings...";
            }
            else
            {
                Storyboard s = (Storyboard)this.TryFindResource("SettingsSlideCloseAnimation");
                s.Begin();

                this.AdvancedSettingsButton.Content = "Show advanced settings...";
            }
        }

        /// <summary>
        /// The debug mode checkbox checked.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void DebugModeCheckboxChecked(object sender, RoutedEventArgs e)
        {
            MainWindow window = (MainWindow)Application.Current.MainWindow;

            window.ToggleDebugMode(SettingManager.Instance.DebugMode);
        }

        /// <summary>
        /// The debug mode checkbox unchecked.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void DebugModeCheckboxUnchecked(object sender, RoutedEventArgs e)
        {
            MainWindow window = (MainWindow)Application.Current.MainWindow;

            window.ToggleDebugMode(SettingManager.Instance.DebugMode);
        }

        /// <summary>
        /// Displays certain update status
        /// </summary>
        /// <param name="status">
        /// Status to be displayed
        /// </param>
        private void DisplayStatus(UpdateDialogStatus status)
        {
            if (this.updateStatus == status)
            {
                return;
            }

            this.updateStatus = status;

            switch (status)
            {
                case UpdateDialogStatus.OutOfDate:

                    // If updater status is idle, we are not doing anything, so we either display
                    // that version is up 2 date, if check was performed less than a day ago,
                    // or a warning if check was displayed more than a day ago
                    DateTime lastCheck = new DateTime(SettingManager.Instance.LastUpdateCheck);

                    this.UpdateImage.Source =
                        new BitmapImage(
                            new Uri(
                                "/PresentationAid;component/Resources/Images/Icons/warning2_16.png", 
                                UriKind.RelativeOrAbsolute));
                    this.UpdateProgressViewbox.Visibility = Visibility.Collapsed;
                    this.UpdateImage.Visibility = Visibility.Visible;

                    this.UpdateDetailTextBlock.Text = "You have last checked for updates at: ";

                    if (lastCheck.Ticks == 0)
                    {
                        this.UpdateDetailTextBlock.Text += "never.";
                    }
                    else
                    {
                        this.UpdateDetailTextBlock.Text += lastCheck.ToShortDateString() + " "
                                                           + lastCheck.ToShortTimeString();
                    }

                    this.UpdateButton.Content = "CHECK FOR UPDATES";
                    this.UpdateButton.IsEnabled = true;

                    break;
                case UpdateDialogStatus.UpToDate:
                    this.UpdateImage.Source =
                        new BitmapImage(
                            new Uri(
                                "/PresentationAid;component/Resources/Images/Icons/check_16.png", 
                                UriKind.RelativeOrAbsolute));
                    this.UpdateProgressViewbox.Visibility = Visibility.Collapsed;
                    this.UpdateImage.Visibility = Visibility.Visible;

                    this.UpdateDetailTextBlock.Text = "PresentationAid is up to date.";

                    this.UpdateButton.Content = "CHECK FOR UPDATES";
                    this.UpdateButton.IsEnabled = true;

                    break;
                case UpdateDialogStatus.Checking:
                    this.UpdateProgressViewbox.Visibility = Visibility.Visible;
                    this.UpdateImage.Visibility = Visibility.Collapsed;

                    this.UpdateProgressRing.State = ProgressState.Busy;

                    this.UpdateDetailTextBlock.Text = "Checking online for updates...";

                    this.UpdateButton.IsEnabled = false;
                    this.UpdateButton.Content = "CHECKING...";

                    break;
                case UpdateDialogStatus.UpdatesAvailable:
                    this.UpdateImage.Source =
                        new BitmapImage(
                            new Uri(
                                "/PresentationAid;component/Resources/Images/Icons/info_16.png", 
                                UriKind.RelativeOrAbsolute));
                    this.UpdateProgressViewbox.Visibility = Visibility.Collapsed;
                    this.UpdateImage.Visibility = Visibility.Visible;

                    this.UpdateDetailTextBlock.Text = "A newer version of PresentationAid is available.";
                    this.UpdateButton.Content = "UPDATE NOW";

                    this.UpdateButton.IsEnabled = true;

                    break;
                case UpdateDialogStatus.Downloading:
                    this.UpdateProgressViewbox.Visibility = Visibility.Visible;
                    this.UpdateImage.Visibility = Visibility.Collapsed;

                    this.UpdateDetailTextBlock.Text = "Downloading updates...";

                    this.UpdateProgressRing.State = ProgressState.Normal;
                    this.UpdateProgressRing.Value = 0;

                    this.UpdateButton.IsEnabled = false;
                    this.UpdateButton.Content = "DOWNLOADING...";

                    break;
                case UpdateDialogStatus.Extracting:
                    this.UpdateProgressViewbox.Visibility = Visibility.Visible;
                    this.UpdateImage.Visibility = Visibility.Collapsed;

                    this.UpdateDetailTextBlock.Text = "Extracting updates...";

                    this.UpdateProgressRing.State = ProgressState.Indeterminate;

                    this.UpdateButton.IsEnabled = false;
                    this.UpdateButton.Content = "EXTRACTING...";

                    break;
                case UpdateDialogStatus.ReadyToApply:
                    this.UpdateImage.Source =
                        new BitmapImage(
                            new Uri(
                                "/PresentationAid;component/Resources/Images/Icons/info_16.png", 
                                UriKind.RelativeOrAbsolute));
                    this.UpdateProgressViewbox.Visibility = Visibility.Collapsed;
                    this.UpdateImage.Visibility = Visibility.Visible;

                    this.UpdateDetailTextBlock.Text = "Update will be installed on next start.";

                    this.UpdateButton.IsEnabled = true;
                    this.UpdateButton.Content = "RESTART NOW";

                    break;
                case UpdateDialogStatus.Error:
                    this.UpdateImage.Source =
                        new BitmapImage(
                            new Uri(
                                "/PresentationAid;component/Resources/Images/Icons/error_16.png", 
                                UriKind.RelativeOrAbsolute));
                    this.UpdateProgressViewbox.Visibility = Visibility.Collapsed;
                    this.UpdateImage.Visibility = Visibility.Visible;

                    this.UpdateDetailTextBlock.Text = "Error during updating, please try again later.";

                    this.UpdateButton.IsEnabled = true;
                    this.UpdateButton.Content = "CHECK FOR UPDATES";

                    break;
            }
        }

        /// <summary>
        /// The gesture engine configure button click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void GestureEngineConfigureButtonClick(object sender, RoutedEventArgs e)
        {
            // Opening settings for gesture engine if possible
            Logger.Debug(
                "Event: GestureEngineConfigureButtonClick - GestureEngine", 
                KinectSensorManager.Instance.GestureEngine.GetType().Name);

            if (!KinectSensorManager.Instance.GestureEngine.ShowSettings())
            {
                Logger.Warn("Event: GestureEngineConfigureButtonClick - UnsupportedSettings");

                MessageBox.Show(
                    Application.Current.MainWindow, 
                    "Sorry, there are no settings available for selected gesture engine.", 
                    "Error", 
                    MessageBoxButton.OK, 
                    MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// The gesture engine selector on selection changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void GestureEngineSelectorOnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Performance improvement
            GestureEngine engine;

            switch (SettingManager.Instance.GestureEngineType)
            {
                default:
                    engine = new PowerPointEngine();

                    break;
                case 1:
                    engine = new ExtendedKeystrokeEngine();

                    break;
                case 2:
                    engine = new KeystrokeEngine();

                    break;
            }

            // If different gesture engine is selected, replace it in manager
            if (engine.GetType() == KinectSensorManager.Instance.GestureEngine.GetType())
            {
                return;
            }

            Logger.Info("Event: GestureEngineSelectionChanged - SelectedEngine: {0}", engine.GetType().Name);

            // KinectSensorManager.Instance.GestureEngine.;
            KinectSensorManager.Instance.GestureEngine = engine;

            KinectSensorManager.Instance.GestureEngine.SetDefaultMapping();
        }

        /// <summary>
        /// When updating has reached final stage, inform user
        /// </summary>
        /// <param name="sender">
        /// Sender of the apply completed event
        /// </param>
        /// <param name="args">
        /// Event arguments
        /// </param>
        private void OnApplyCompleted(object sender, OperationEventArgs args)
        {
            if (args.Exception == null)
            {
                Logger.Info("Event: ApplyComplete");

                this.DisplayStatus(UpdateDialogStatus.ReadyToApply);
            }
            else
            {
                Logger.WarnException("Event: ApplyComplete", args.Exception);
            }
        }

        /// <summary>
        /// Handles download progress event
        /// </summary>
        /// <param name="sender">
        /// Sender of download progress event
        /// </param>
        /// <param name="args">
        /// Event arguments
        /// </param>
        private void OnDownloadProgress(object sender, OperationEventArgs args)
        {
            // Logger.Trace ("Event: DownloadProgress - Progress: {0}", args.Progress);
            this.UpdateProgressRing.Value = args.Progress;
        }

        /// <summary>
        /// Handles specific status changes when updating is in progress (except for downloads and checks)
        /// </summary>
        /// <param name="sender">
        /// Sender of status changed event
        /// </param>
        /// <param name="eventArgs">
        /// Event arguments
        /// </param>
        private void OnStatusChanged(object sender, StatusEventArgs eventArgs)
        {
            if (eventArgs.Exception != null)
            {
                this.DisplayStatus(UpdateDialogStatus.Error);

                Logger.WarnException("Event: StatusChanged - status: {0}", eventArgs.Exception);
            }
            else
            {
                Logger.Trace("Event: StatusChanged - status: {0}", UpdateManager.Instance.Status);

                switch (UpdateManager.Instance.Status)
                {
                    case UpdateStatus.Verifying:
                    case UpdateStatus.Preparing:
                    case UpdateStatus.Applying:
                        this.DisplayStatus(UpdateDialogStatus.Extracting);

                        break;
                }
            }
        }

        /// <summary>
        /// Event happens when update check is finished
        /// </summary>
        /// <param name="sender">
        /// Sender of update check finished event
        /// </param>
        /// <param name="args">
        /// Event arguments
        /// </param>
        private void OnUpdateCheckFinished(object sender, OperationEventArgs args)
        {
            if ((args.UpdateInfo != null) && (args.UpdateInfo.AvailableVersions.Count > 0))
            {
                Logger.Debug(
                    "Event: UpdateCheckFinished - UpdateInfoVersionCount: {0}", args.UpdateInfo.AvailableVersions.Count);

                this.updateInfo = args.UpdateInfo;

                this.DisplayStatus(UpdateDialogStatus.UpdatesAvailable);
            }
            else
            {
                Logger.WarnException("Event: UpdateCheckFinished - NoUpdateVersion", args.Exception);

                this.DisplayStatus(UpdateDialogStatus.UpToDate);
            }
        }

        /// <summary>
        /// The update button click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void UpdateButtonClick(object sender, RoutedEventArgs e)
        {
            Logger.Info("Event: UpdateButtonClick - CurrentUpdateStatus: {0}", this.updateStatus.ToString());

            // Button was clicked, action depends on status, please note that
            // the update status of the settings tab does not respond to all
            // of the status changed events of the manager.
            switch (this.updateStatus)
            {
                case UpdateDialogStatus.Error:
                case UpdateDialogStatus.OutOfDate:
                case UpdateDialogStatus.UpToDate:
                    this.DisplayStatus(UpdateDialogStatus.Checking);

                    UpdateManager.Instance.CheckForUpdates();

                    Logger.Debug("EventDetail: UpdateButtonClick - CheckForUpdates");

                    break;
                case UpdateDialogStatus.UpdatesAvailable:
                    this.DisplayStatus(UpdateDialogStatus.Downloading);

                    UpdateManager.Instance.Update(this.updateInfo);

                    Logger.Debug("EventDetail: UpdateButtonClick - UpdateApplicationAction");

                    break;
                case UpdateDialogStatus.ReadyToApply:
                    Application.Current.Shutdown();

                    break;
            }
        }

        #endregion
    }
}